package services

import (
	"context"
	"errors"
	"fmt"
	"go-caipu/pkg/infra/log"
	"go-caipu/pkg/services/admin/model"
	datasource "go-caipu/pkg/services/datasources"
	"go-caipu/pkg/services/datasources/models"
	"go-caipu/pkg/services/dto"
	"gorm.io/gorm"
)

type store interface {
	GetDatasourcePage(ctx context.Context, cmd datasource.GetDatasourcePageCommand) (data []datasource.Datasource, num int64, err error)
	Get(ctx context.Context, cmd datasource.GetDatasourceCommand) (datasource.Datasource, error)
	GetByName(ctx context.Context, name string) (datasource.Datasource, error)
	Insert(ctx context.Context, post datasource.Datasource) (datasource.Datasource, error)
	Update(ctx context.Context, post datasource.Datasource) error
	Remove(ctx context.Context, cmd datasource.RemoveDatasourceCommand) error
	RemoveDSByPluginID(ctx context.Context, pluginID string, categorys []int64, apiIds []int64, menuIDs []int64) (err error)
}
type sqlStore struct {
	db     *gorm.DB
	logger log.Logger
}

func ProvideStore(db *gorm.DB) sqlStore {
	return sqlStore{
		db:     db,
		logger: log.New("dept.store"),
	}
}

func (s sqlStore) GetDatasourcePage(ctx context.Context, cmd datasource.GetDatasourcePageCommand) (result []datasource.Datasource, num int64, err error) {
	var items []models.Datasource
	err = s.db.Model(&models.Datasource{}).Scopes(
		dto.MakeCondition(cmd.GetNeedSearch()),
		dto.Paginate(cmd.GetPageSize(), cmd.GetPageIndex()),
	).Find(&items).Limit(-1).Offset(-1).Count(&num).Error
	if err != nil {
		return
	}
	for _, v := range items {
		result = append(result, ConvertToDTO(v))
		//SecureJsonData

	}
	return
}
func ConvertToDTO(model models.Datasource) datasource.Datasource {
	result := datasource.Datasource{
		Id:             model.Id,
		Version:        model.Version,
		Type:           model.Type,
		Name:           model.Name,
		Access:         model.Access,
		Url:            model.Url,
		IsDefault:      model.IsDefault,
		JsonData:       model.JsonData,
		SecureJsonData: model.SecureJsonData,
		ReadOnly:       model.ReadOnly,
		ControlBy:      model.ControlBy,
		ModelTime:      model.ModelTime,
	}
	return result
}
func (s sqlStore) Get(ctx context.Context, cmd datasource.GetDatasourceCommand) (result datasource.Datasource, err error) {
	var data models.Datasource
	err = s.db.Model(&data).Where("type = ?", cmd.Id).First(&data).Error
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		err = errors.New(fmt.Sprintf("datasource: %d record Not found", cmd.Id))
		return
	}
	if err != nil {
		s.logger.Log(fmt.Sprintf("GetByHash datasource: %d  error:%s", cmd.Id, err.Error()))
		return
	}
	return ConvertToDTO(data), nil
}
func (s sqlStore) GetByName(ctx context.Context, name string) (result datasource.Datasource, err error) {
	var data models.Datasource
	err = s.db.Model(&models.Datasource{}).Where("type = ?", name).First(&data).Error
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		err = datasource.ErrDatasourceNotFound
		return
	}
	return ConvertToDTO(data), nil
}
func ConvertToDT(model datasource.Datasource, cmd *models.Datasource) {
	cmd.Id = model.Id
	cmd.Version = model.Version
	cmd.Type = model.Type
	cmd.Name = model.Name
	cmd.Access = model.Access
	cmd.Url = model.Url
	cmd.IsDefault = model.IsDefault
	cmd.JsonData = model.JsonData
	cmd.SecureJsonData = model.SecureJsonData
	cmd.ReadOnly = model.ReadOnly
}

// Insert 插入插件
func (s sqlStore) Insert(ctx context.Context, cmd datasource.Datasource) (ds datasource.Datasource, err error) {
	tx := s.db.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()

	var data models.Datasource
	//exists ds
	err = s.db.Model(&data).Where(" type = ?", cmd.Type).First(&data).Error
	if err == nil {
		return ConvertToDTO(data), nil
	}
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return
	}

	//no exists ds
	ConvertToDT(cmd, &data)
	data.CreatedAt = cmd.CreatedAt
	data.CreateBy = cmd.CreateBy

	err = tx.Create(&data).Error
	if err != nil {
		s.logger.Log(fmt.Sprintf("insert  datasource  error:%s", err.Error()))
		return
	}
	return
}

func (s sqlStore) Update(ctx context.Context, cmd datasource.Datasource) (err error) {
	tx := s.db.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	//查询当前部门名称是否存在
	var data models.Datasource
	err = s.db.Model(&data).First(&data, cmd.Id).Error
	if err != nil {
		s.logger.Log(fmt.Sprintf("check Datasource id=%d is exists error:%s", cmd.Id, err.Error()))
		return err
	}
	ConvertToDT(cmd, &data)
	data.UpdatedAt = cmd.UpdatedAt
	data.UpdateBy = cmd.UpdateBy
	err = tx.Omit("type").Save(&data).Error
	if err != nil {
		s.logger.Log(fmt.Sprintf("update  datasource  error:%s", err.Error()))
		return err
	}
	return nil
}

func (s sqlStore) Remove(ctx context.Context, cmd datasource.RemoveDatasourceCommand) (err error) {
	tx := s.db.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	var data models.Datasource
	err = tx.Model(&models.Datasource{}).Delete(&data, cmd.Ids).Error
	if err != nil {
		return err
	}
	return
}

// RemoveDSByPluginID 根据插件ID远程删除数据源。
// 此函数接收上下文、插件ID、API列表和菜单ID列表作为参数，
// 目前函数体为空，需要实现具体的删除逻辑。
// 参数:
// - ctx: 上下文，用于控制请求的生命周期和传递请求范围的数据。
// - pluginID: 插件的唯一标识符。
// - apiIds: 相关API的字符串切片。
// - menuIDs: 菜单ID的字符串切片。
// 返回值:
// - err: 操作过程中可能出现的错误。
func (s sqlStore) RemoveDSByPluginID(ctx context.Context, pluginID string, categorys []int64, apiIds []int64, menuIDs []int64) (err error) {
	tx := s.db.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	//fix:MySQL在执行操作时，遇到了不兼容的字符集或排序规则（collation）导致系统报错
	//【暂不处理，权限认证通过，但是调不通】

	// 子查询：获取 sys_api 表中 path 的值，且 id 在给定范围内
	//subQuery := tx.Table("sys_api").Select("path").Where("id IN (?)", apiIds)
	//err = tx.Table("casbin_rule").Where("v1 IN (?)", subQuery).Delete(&model.SysCasbinRule{}).Error
	err = tx.Where("sys_api_id IN (?)", apiIds).Delete(&model.SysMenuAPI{}).Error
	if err != nil {
		return err
	}
	err = tx.Where("sys_menu_menu_id IN (?)", menuIDs).Delete(&model.SysMenuAPI{}).Error
	if err != nil {
		return err
	}
	err = tx.Where("menu_Id IN (?)", menuIDs).Delete(&model.SysRoleMenu{}).Error
	if err != nil {
		return err
	}
	err = tx.Where("menu_Id IN (?)", menuIDs).Delete(&model.SysMenu{}).Error
	if err != nil {
		return err
	}
	err = tx.Where("id IN (?)", apiIds).Delete(&model.SysApi{}).Error
	if err != nil {
		return err
	}
	err = tx.Where("id IN (?)", categorys).Delete(&model.SysApiCategory{}).Error
	if err != nil {
		return err
	}

	//删除数据源配置 by 2025-7-7
	err = tx.Where("name =?", pluginID).Delete(&models.Datasource{}).Error
	if err != nil {
		return err
	}

	return nil
}
